package com.zx.idc.backend.gui.api.commom.template;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zx.idc.backend.gui.api.commom.base.BaseIDCCommandService;
import com.zx.idc.backend.gui.api.commom.entity.CommandModel;
import com.zx.idc.backend.gui.api.commom.factory.HashProvider;
import com.zx.idc.backend.gui.api.ws.smms.client.src.com.surfilter.ismi.webservice.IDCWebServiceStub;
import com.zx.idc.backend.gui.sys.login.MailClient;
import com.zx.idc.common.attributes.SchemaFileNameEnum;
import com.zx.idc.common.spring.SpringBeanUtil;
import com.zx.idc.common.util.EncryptionUtils;
import com.zx.idc.ds.base.oper.service.IBaseOperCompanyService;
import com.zx.idc.ds.tlog.entity.TLogAlarm;
import com.zx.idc.ds.tlog.factory.TLogAlarmFactory;
import com.zx.idc.ds.tlog.service.ITLogAlarmService;
import com.zx.idc.xml.entity.Return;
import com.zx.idc.xml.utils.JaxbUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

import static com.zx.idc.common.attributes.InterfaceParamConfiguration.*;
import static com.zx.idc.common.attributes.SystemAttributes.RANDOM_STRING_LENGTH;
import static com.zx.idc.common.attributes.SystemAttributes.WEB_SERVICE_RE_SEND_INSTRUCTION_THRESHOLD_VAL;
import static com.zx.idc.common.attributes.WebServiceInterfaceAttributes.WEB_SERVICE_INTERFACE_VERSION;
import static com.zx.idc.common.attributes.WebServiceReturnCodeAttributes.OK;
import static com.zx.idc.common.attributes.WebServiceReturnCodeAttributes.OTHER_EXCEPTION;


/**
 * Created by hasee on 2019/2/23.
 * 调用smms的webService接口模板
 */
@Component
public class IdcCommandTransferTemplate extends BaseIDCCommandService {

    @Autowired
    private MailClient mailClient;
    @Autowired
    private IBaseOperCompanyService iBaseOperCompanyService;

    private static final int reCallNum = WEB_SERVICE_RE_SEND_INSTRUCTION_THRESHOLD_VAL;


    /**
     * 调用IDC接口返回结果模板
     *
     * @param commandModel 传过来的指令实体类
     */
    public <R> void transferTemplate(CommandModel commandModel, R r, SchemaFileNameEnum schemaFileNameEnum) {
        //1.认证
        LOG.debug("调用IDC接口返回结果模板执行");
        String randVal = randomString(RANDOM_STRING_LENGTH);
        String certificationInfo = stitching(getVal(USER_PASSWORD), randVal);
        LOG.debug("用户口令+随机数:{}", certificationInfo);
        Integer hashNum = Integer.valueOf(getVal(HASH_ALGORITHM));
        LOG.debug("hash算法类型:{}", hashNum);
        Integer compressionFormatNum = Integer.valueOf(getVal(COMPRESSION_FORMAT));
        LOG.debug("压缩算法类型:{}", compressionFormatNum);
        Integer encryptionAlgorithmNum = Integer.valueOf(getVal(ENCRYPTION_ALGORITHM));
        LOG.debug("加密算法类型:{}", encryptionAlgorithmNum);
        String encryptionKey = getVal(ENCRYPTION_KEY);
        LOG.debug("加密秘钥:{}", encryptionKey);
        String key = getVal(AUTHENTICATION_KEY);
        LOG.debug("认证秘钥:{}", key);

        String pwdHash = HashProvider.hashByBase64(hashNum, certificationInfo);
        LOG.debug("hash之后的认证信息:{}", pwdHash);
        //2.压缩指令
        String schemaFilePath = getSchemaFilePath(schemaFileNameEnum.getSchemaFileName());
        //转为xml字符串的指令结果内容
        String commandText = JaxbUtils.beanToXml(r, schemaFilePath);
        LOG.debug("转为xml字符串的指令结果内容:{}", commandText);
        //压缩后的信息
        byte[] enCmpBytes = enCmp(compressionFormatNum, commandText);
        LOG.debug("压缩后的信息:{}", enCmpBytes);
        //压缩数据拼接秘钥
        byte[] bytes = ArrayUtils.addAll(enCmpBytes, key.getBytes());
        LOG.debug("压缩数据拼接秘钥:{}", bytes);
        //returnHash
        String returnHash = HashProvider.hashByBase64(hashNum, bytes);
        LOG.debug("Base64后的returnHash:{}", returnHash);
        //result
        String encodeResultText = encode(encryptionAlgorithmNum, enCmpBytes, encryptionKey, getVal(ASE_OFFSET));
        LOG.debug("机密后的密文:{}", encodeResultText);
        String idcId = iBaseOperCompanyService.getIdcId();
        IDCWebServiceStub.Idc_commandack idc_commandack = new IDCWebServiceStub.Idc_commandack();
        idc_commandack.setRandVal(randVal);
        idc_commandack.setPwdHash(pwdHash);
        idc_commandack.setResult(encodeResultText);
        idc_commandack.setResultHash(returnHash);
        //设置接口对接信息
        idc_commandack.setCommandVersion(getConfig(WEB_SERVICE_INTERFACE_VERSION));
        idc_commandack.setIdcId(idcId);
        idc_commandack.setCompressionFormat(compressionFormatNum);
        idc_commandack.setHashAlgorithm(hashNum);
        idc_commandack.setEncryptAlgorithm(encryptionAlgorithmNum);
        LOG.debug("调用管局接口");
        sendInstructionN(idc_commandack, getSchemaFilePath(SchemaFileNameEnum.WEBSERVICE_RETURN.getSchemaFileName()), getIdcCallbackCommandAddress());
    }

    /**
     * 发送指令，返回900状态码则重新发布，阈值在
     * SystemAttributes.WEB_SERVICE_RE_SEND_INSTRUCTION_THRESHOLD_VAL
     * 设置，超过阈值则结束调用
     *  @param idc_commandack
     * @param schemaPath
     * @param address
     */
    private void sendInstructionN(IDCWebServiceStub.Idc_commandack idc_commandack, String schemaPath, String address) {
        Return aReturn = null;

        for (int i = 1; i <= reCallNum; i++) {
            try {
                LOG.debug("根据管局webService地址调用接口 地址:{} 调用第{}次", address, i);
                LOG.debug("接口参数: idcId:{} randVal:{} pwdHash:{} result:{} resultHash:{} encryptAlgorithm:{} hashAlgorithm:{} compressionFormat:{} commandVersion:{}", idc_commandack.getIdcId(), idc_commandack.getRandVal(), idc_commandack.getPwdHash(), idc_commandack.getResult(), idc_commandack.getResultHash(), idc_commandack.getEncryptAlgorithm(), idc_commandack.getHashAlgorithm(), idc_commandack.getCompressionFormat(), idc_commandack.getCommandVersion());
                IDCWebServiceStub idcWebServiceStub = new IDCWebServiceStub();
                IDCWebServiceStub.Idc_commandackE idc_commandackE = new IDCWebServiceStub.Idc_commandackE();
                idc_commandackE.setIdc_commandack(idc_commandack);
                IDCWebServiceStub.Idc_commandackResponseE idc_commandackResponseE = idcWebServiceStub.idc_commandack(idc_commandackE);
                IDCWebServiceStub.Idc_commandackResponse idc_commandackResponse = idc_commandackResponseE.getIdc_commandackResponse();
                String aReturnStr = idc_commandackResponse.get_return();
                LOG.debug("接口调用结果xml: {}", aReturnStr);
                aReturn = JaxbUtils.xmlToBean(aReturnStr, Return.class, null);
                LOG.debug("接口调用结果实体: {}", aReturn);
                if (!OTHER_EXCEPTION.equals(aReturn.getResultCode())) {
                    break;
                }
                LOG.debug("接口需要重新发送");
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
                throw new RuntimeException("调用管局接口异常!", e);
            }
        }
        if (aReturn != null) {
            if (!OK.equals(aReturn.getResultCode())) {
                LOG.error("调用成功，但指令存在问题，添加到告警表中");
                ITLogAlarmService itLogAlarmService = SpringBeanUtil.getBean(ITLogAlarmService.class);
                TLogAlarm tLogAlarm = TLogAlarmFactory.getCallingInterfaceFailed("调用管局接口异常,异常码为 " + aReturn.getResultCode());
                try {
                    itLogAlarmService.insertTLogAlarm(tLogAlarm, IOUtils.toInputStream(idc_commandack.getResult(), StandardCharsets.UTF_8.name()), getFileName(schemaPath, tLogAlarm.getAlarmTime()));
                    mailClient.sendAlarmEmail("有新的告警日志生成，来自调用管局接口", "具体请查看ID为" + tLogAlarm.getId() + "的告警日志。");
                } catch (IOException e) {
                    LOG.error("告警功能 指令字符串转为流异常", e);
                }
            }
            LOG.debug("调用成功!");
        }
    }

    private String getFileName(String schemaPath, LocalDateTime alarmTime) {
        //获取schema的文件名
        String[] split = schemaPath.split("/");
        return new StringBuilder()
            .append(split[split.length - 1])
            .append("_")
            .append(alarmTime.toInstant(ZoneOffset.of("+8")).toEpochMilli())
            .toString();
    }

}
